package com.itheima.leyou.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.itheima.leyou.common.constants.MQConstants;
import com.itheima.leyou.common.dto.PageDTO;
import com.itheima.leyou.common.exception.LyException;
import com.itheima.leyou.common.utils.JsonUtils;
import com.itheima.leyou.mapper.SpuMapper;
import com.itheima.leyou.po.Sku;
import com.itheima.leyou.po.Spu;
import com.itheima.leyou.po.SpuDetail;
import com.itheima.leyou.service.*;
import com.leyou.item.dto.*;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SpuServiceImpl extends ServiceImpl<SpuMapper, Spu> implements SpuService {

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private BrandService brandService;

    @Autowired
    private SpuService spuService;

    @Autowired
    private SpecGroupService specGroupService;

    @Autowired
    private SpecParamService specParamService;

    @Autowired
    private SpuDetailService spuDetailService;

    @Autowired
    private SkuService skuService;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Override
    public PageDTO<SpuDTO> queryPage(Integer page, Integer rows, Boolean saleable, Long brandId, Long categoryId, Long id) {
        IPage<Spu> spuPage = new Page<>(page, rows);
        page(spuPage, new QueryWrapper<Spu>()
                .eq(null != saleable, "saleable", saleable)
                .eq(null != brandId, "brand_id", brandId)
                .eq(null != categoryId, "cid3", categoryId)
                .eq(null != id, "id", id)
        );
        List<SpuDTO> spuDTOS = SpuDTO.convertEntityList(spuPage.getRecords());
        spuDTOS.forEach(spuDTO -> {
            //根据id的集合查询对应的分类集合,去除分类名称拼接为字符串
            String categoryName = this.categoryService
                    .queryCategoryByIds(spuDTO.getCategoryIds())
                    .stream()
                    .map(CategoryDTO::getName)
                    .collect(Collectors.joining("/"));//自己可以注意下
            spuDTO.setCategoryName(categoryName);
            //存brandName到spuDTO中
            spuDTO.setBrandName(this.brandService
                    .getById(spuDTO.getBrandId())//一定要注意，不能写错了，是brandid
                    .getName());
        });

        return new PageDTO<>(spuPage.getTotal()
                , spuPage.getPages()
                , spuDTOS);
    }

    @Override
    public List<SpecGroupDTO> listSpuBySpuId(Long spuid) {
        //根据spuid查询分类id
        Long cid3 = spuService.getById(spuid).getCid3();

        //根据分类id查询当前分类下所有的规格参数组
        List<SpecGroupDTO> specGroupDTOS = SpecGroupDTO.convertEntityList(this.
                specGroupService
                .query()
                .eq("category_id", cid3).list());


        //根据分类id查询当前分类下所有的规格参数
        List<SpecParamDTO> specParamDTOS = SpecParamDTO.convertEntityList(
                this.specParamService
                        .query()
                        .eq("category_id", cid3).list());


        //对规格参数集合进行分组，将统一groupid的分为一组，key为groupid,value是统一组id的集合
        Map<Long, List<SpecParamDTO>> listMap = specParamDTOS
                .stream()
                .collect(Collectors.groupingBy(SpecParamDTO::getGroupId));


        //获取规格参数的value
        SpuDetail spuDetail = this.spuDetailService.getById(spuid);

        //将SpuDetail转换为map键值对的形式
        Map<Long, Object> mapValue = JsonUtils.nativeRead(spuDetail.getSpecification(),
                new TypeReference<Map<Long, Object>>() {
                });


        //遍历specParamDTOList,并给每一个规格参数赋值
        specParamDTOS.forEach(specParamDTO -> {
            //规格参数的id
            Long key = specParamDTO.getId();
            //赋值
            specParamDTO.setValue(mapValue.get(key));
        });


        //规格参数集合存入到规格参数组中
        specGroupDTOS.forEach(specGroupDTO -> {
            specGroupDTO.setParams(listMap.get(specGroupDTO.getId()));
        });

        return specGroupDTOS;
    }

    @Override
    public SpuDTO querySpuById(Long id) {
        SpuDTO spuDTO = new SpuDTO(this.getById(id));
        if(!StringUtils.isEmpty(spuDTO)){
            Long cid3 = spuDTO.getCid3();
            String categoryName = categoryService.getById(cid3).getName();
            spuDTO.setCategoryName(categoryName);
            String brandName = brandService.getById(spuDTO.getBrandId()).getName();
            spuDTO.setBrandName(brandName);
        }
        return spuDTO;
    }

    @Override
    @Transactional
    public void addSpu(SpuDTO spuDTO) {
        boolean success;
        //新增spu表
        spuDTO.setSaleable(true);
        Spu spu = spuDTO.toEntity(Spu.class);
        success = this.save(spu);
        if(!success){
            throw  new LyException(500,"新增商品失败");
        }

        //新增detail表
        Long id = spu.getId();
        SpuDetailDTO spuDetailDTO = spuDTO.getSpuDetail();
        SpuDetail spuDetail = spuDetailDTO.toEntity(SpuDetail.class);
        spuDetail.setSpuId(id);
         success = spuDetailService.save(spuDetail);
        if(!success){
            throw  new LyException(500,"新增商品详情表失败");
        }

        //新增sku表
        List<Sku> skus = spuDTO.getSkus().stream().map(skuDTO -> {
            Sku sku = skuDTO.toEntity(Sku.class);
            sku.setSpuId(id);
            sku.setSaleable(spu.getSaleable());//设置上下架
            return sku;
        }).collect(Collectors.toList());
        success = skuService.saveBatch(skus);
        if(!success){
            throw  new LyException(500,"新增商品特征表失败");
        }
    }

    @Override
    @Transactional
    public void updateSpu(SpuDTO spuDTO) {
        //修改spu
        if(spuDTO.getId()!=null){
            Spu spu = spuDTO.toEntity(Spu.class);
            spu.setUpdateTime(new Date());
            spuService.updateById(spu);
        }


        //修改detail表
        SpuDetailDTO detailDTO = spuDTO.getSpuDetail();
        if(detailDTO!=null){
            SpuDetail spuDetail = detailDTO.toEntity(SpuDetail.class);
          spuDetailService.updateById(spuDetail);

        }

        //修改 sku表
        if(CollectionUtils.isEmpty(spuDTO.getSkus())){
            throw new LyException(204,"至少选一条sku属性");
        }

        List<Sku> skus = spuDTO.getSkus().stream().map(skuDTO -> {
            Sku sku = skuDTO.toEntity(Sku.class);
            return sku;
        }).collect(Collectors.toList());

        //通过saleable==null对skus进分组，如果==null，表示是删除，!=null,表示修改或者删除
        Map<Boolean, List<Sku>> listMap = skus.stream()
                .collect(Collectors
                        .groupingBy(sku ->
                                sku.getSaleable() == null));
        //true，删除
        List<Sku> skuList = listMap.get(true);
        if(!CollectionUtils.isEmpty(skuList)){
            skuService.removeByIds(skuList.stream().map(Sku::getId).collect(Collectors.toList()));
        }

        //false,修改或添加
        List<Sku> skus1 = listMap.get(false);
        if(!CollectionUtils.isEmpty(skus1)){
            skuService.saveOrUpdateBatch(skus1);
        }
    }

    @Override
    public void updateUpOrDownSpu(Long id, Boolean saleable) {

        //更新商品
        Spu spu = new Spu();
        spu.setId(id);
        spu.setSaleable(saleable);
        boolean flag = this.spuService.updateById(spu);
        if(!flag){
            throw new LyException(400,"更新商品失败");
        }

        //更新sku表
        boolean b = this.skuService
                .update()
                .eq("spu_id", id)
                .set("saleable", saleable)
                .update();
        if(!b){
            throw new LyException(400,"更新sku表失败");
        }

        // 3.发送MQ消息
        String routingKey = saleable ? MQConstants.RoutingKeyConstants.ITEM_UP_KEY : MQConstants.RoutingKeyConstants.
                ITEM_DOWN_KEY;
        //交换机， routingKey,id
        amqpTemplate.convertAndSend(MQConstants.ExchangeConstants.ITEM_EXCHANGE_NAME, routingKey, id);

    }

    @Override
    public SpuDTO queryGoods(Long spuId) {
        //根据商品id查询spu表信息
        PageDTO<SpuDTO> pageDTO = this.queryPage(1, 1, null, null, null, spuId);
        List<SpuDTO> items = pageDTO.getItems();
        //一个spuId对应一条spu信息
        SpuDTO spuDTO = items.get(0);
        //查询spuDetail信息
        SpuDetailDTO spuDetailDTO = spuDetailService.queryDetailById(spuId);

            spuDTO.setSpuDetail(spuDetailDTO);

        //查询skus
        List<SkuDTO> skuDTOS = skuService.querySkuByspuId(spuId);
        spuDTO.setSkus(skuDTOS);
        return spuDTO;
    }

    @Override
    public List<SpecParamDTO> listSpec(Long spuId, Boolean searching) {

        //spuId获取到对应的规格参数，以及规格参数值，规格参数组
        ;
        //查询spu获取分类id
        Long cid = getById(spuId).getCid3();

        //跟分类id查询当前分类下所有的规格参数组

        //普通，遍历规格参数组，根据每个组id查询对应的组内参数

        //跟分类id查询当前分类下所有的规格参数
        List<SpecParamDTO> specParamDTOS = SpecParamDTO
                .convertEntityList(this.specParamService
                        .query()
                        .eq("category_id", cid)
                        .eq(null != searching, "searching", searching)
                        .list());

        //处理每一个specParam的value，？？在spuDetail中


        //对规格参数集合，进行分组，已groupId分组，同id，为一组,key为groupId，value同id一组集合


        //获取规格参数的value，
        SpuDetail spuDetail = this.spuDetailService.getById(spuId);

        //json,class

        //nativeRead 第一个参数，接送，第二个参数，类型限定
        //key 规格参数的id，value，规格参数的值
        Map<Long, Object> valueMap = JsonUtils
                .nativeRead(spuDetail
                        .getSpecification(), new TypeReference<Map<Long, Object>>() {
        });

        //遍历specParamDTO，给每个value赋值，每个value的值，就是valuemap的value
        specParamDTOS.forEach(specParamDTO -> {

            //规格参数的id
            Long key = specParamDTO.getId();

            specParamDTO.setValue(valueMap.get(key));
        });

        return specParamDTOS;
    }
}
